﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.CompositeUI.Properties;
using Microsoft.Practices.CompositeUI.Utility;

namespace Microsoft.Practices.CompositeUI.Commands
{
    /// <summary>
    /// 	This is the <see cref = "ICommandAdapterMapService" /> default implementation.
    /// </summary>
    public class CommandAdapterMapService : ICommandAdapterMapService
    {
        #region Fields & Properties

        private readonly Dictionary<Type, Type> _typeMapping = new Dictionary<Type, Type>();

        #endregion

        #region Implementation of ICommandAdapterMapService

        /// <summary>
        /// 	Registers an adapter type to be created for the specified invoker type.
        /// </summary>
        /// <param name = "invokerType">The invoker type to associate with the specified adapter type. 
        /// 	This type can be any type.</param>
        /// <param name = "adapterType">Any type implementing the <see cref = "CommandAdapter" /> interface.</param>
        public void Register(Type invokerType, Type adapterType)
        {
            Guard.ArgumentNotNull(invokerType, "invokerType");
            Guard.ArgumentNotNull(adapterType, "adapterType");
            if (!typeof (CommandAdapter).IsAssignableFrom(adapterType))
            {
                throw new CommandAdapterMapServiceException(Resources.InvalidAdapter);
            }
            _typeMapping[invokerType] = adapterType;
        }

        /// <summary>
        /// 	Removes the adapter registration for the specified invoker type.
        /// </summary>
        /// <param name = "invokerType">The invoker type to remove.</param>
        public void UnRegister(Type invokerType)
        {
            Guard.ArgumentNotNull(invokerType, "invokerType");
            _typeMapping.Remove(invokerType);
        }

        /// <summary>
        /// 	Creates a new <see cref = "CommandAdapter" /> instance for the specified invoker type.
        /// </summary>
        /// <param name = "invokerType">The invoker type to create an adapter for.</param>
        /// <returns>A new <see cref = "CommandAdapter" /> instance for the registered invoker;
        /// 	null if there is no adapter registration for the required invoker type.</returns>
        public CommandAdapter CreateAdapter(Type invokerType)
        {
            Guard.ArgumentNotNull(invokerType, "invokerType");
            CommandAdapter adapter;
            if (_typeMapping.ContainsKey(invokerType))
            {
                adapter = (CommandAdapter) Activator.CreateInstance(_typeMapping[invokerType]);
            }
            else
            {
                adapter = FindAssignableAdapter(invokerType);
            }
            return adapter;
        }

        private CommandAdapter FindAssignableAdapter(Type type)
        {
            return _typeMapping.Where(pair => pair.Key.IsAssignableFrom(type)).Select(pair => (CommandAdapter) Activator.CreateInstance(pair.Value)).FirstOrDefault();
        }

        #endregion
    }
}